package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"log"
	"os"
	"protobuf-learn/internal/auth"
	"protobuf-learn/proto"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

func main() {
	ctx := context.Background()

	jwt, ok := os.LookupEnv("JWT_SECRET")
	if !ok {
		log.Fatal("JWT_SECRET env var missing")
	}
	authService, err := auth.NewService(jwt)
	if err != nil {
		log.Fatal(err)
	}

	token, err := authService.IssueToken(ctx, "user-id-123456")
	if err != nil {
		log.Fatal(err)
	}

	// load tls from privite CA
	certPool := x509.NewCertPool()
	cert, err := os.ReadFile("certs/ca.crt")
	if err != nil {
		log.Fatal(err)
	}
	if ok := certPool.AppendCertsFromPEM(cert); !ok {
		log.Fatal("failed to append CA cert")
	}

	clientCert, err := tls.LoadX509KeyPair("certs/client.crt", "certs/client.key")
	if err != nil {
		log.Fatal(err)
	}

	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{clientCert},
		RootCAs:      certPool,
	}

	tlsCredentials := credentials.NewTLS(tlsConfig)

	host := ":50051"
	conn, err := grpc.NewClient(host,
		//grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithTransportCredentials(tlsCredentials),
		// grpc.WithUnaryInterceptor(
		// 	func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
		// 		log.Printf("sending request: %s", method)
		// 		err := invoker(ctx, method, req, reply, cc, opts...)
		// 		log.Printf("response received from server: %s", method)
		// 		return err
		// 	}),
		grpc.WithChainUnaryInterceptor(
			func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
				start := time.Now()
				err := invoker(ctx, method, req, reply, cc, opts...)
				log.Printf("response %s took %s", method, time.Since(start))
				return err
			},
			func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
				log.Printf("sending request: %s", method)
				err := invoker(ctx, method, req, reply, cc, opts...)
				log.Printf("response received from server: %s", method)
				return err
			},
		),
		grpc.WithChainStreamInterceptor(
			func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
				start := time.Now()
				clientStream, err := streamer(ctx, desc, cc, method, opts...)
				log.Printf("stream response %s took %s", method, time.Since(start))
				return clientStream, err
			},
			func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
				log.Printf("sending request: %s", method)
				clientStream, err := streamer(ctx, desc, cc, method, opts...)
				log.Printf("response received from server: %s", method)
				return clientStream, err
			},
		),
	)

	if err != nil {
		log.Fatal(err)
	}

	defer conn.Close()

	client := proto.NewInterceptorServiceClient(conn)

	ctx = metadata.AppendToOutgoingContext(ctx, "authorization", token)
	res, err := client.Protected(ctx, &proto.ProtectedRequest{})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("response with userId: %s", res.GetUserId())
}
